/* Read msg from DB, send it to somebody using short message 
 * created at 2007-11-16
 * revised by at 2008-04-22
 */
#include <stdio.h>      
#include <stdlib.h>   
#include <unistd.h>     
#include <signal.h>
#include <time.h>
#include <sys/types.h>  
#include <sys/stat.h>   
#include <fcntl.h>    
#include <termios.h>  
#include <errno.h>  
#include <termios.h>	
#include <mysql/mysql.h>
#include <string.h>
#include "gsm_sms.h"
#include "text.h"

char serialdevice[64] = "/dev/ttyUSB0";

//mysql params
char host[256] = "localhost";
char user[256] = "monitor";
char passwd[256] = "xxxxxxx";
char database[256] = "monitor";
char msg_to_send[256] = "msg_to_send";
char msg_have_sended[256] = "msg_have_sended";

int  time_interval = 60;                  //time interval to read new errmsg from database
char sms_center[16] = "8613800571500";   //china unicom short message center,china mobile is 8613800571500
int  max_msg_len = 256;                 //max sms length,it can bigger,program will split msg if too long

static int fd;              //file descriptor of serialdevice
FILE* logfile;

MYSQL mysql;     // need a instance to init
MYSQL_RES *res;
MYSQL_ROW row;
int t,ret;

#define MAXQUERYLENGTH 256  //max length of a sql query
char query[MAXQUERYLENGTH]; 

void WriteLogHead();

/*Send msg to cell phone
 *Notice: the msg maybe very long, we should send part of it at a time.
          if the msg is PDU, it must be GB2312 format, else it cannot show correctly in your cellphone
 *Return Value: 
         0 success
        -1  write /dev/ttyS0 failure
				-2  parameter msg is not correct
				-3  parameter phone is wrong
				-4  return error from AT instruction 
 */
int SendSMS(char* msg,char* phone)
{
  if(!msg || strlen(msg) < 1)
	  return -2;
  //maybe send to special number such as 10011 to check account remain money
  if(!phone /*|| strlen(phone) < 11 || strlen(phone) > 14*/)  
	  return -3;

  SM_PARAM *pSM;
  SM_PDU pdu;
  size_t len;                             //bytes of msg in ucs2 format
  const size_t bufsize = max_msg_len*2+1;         
  char buf[bufsize];                   //store msg in ucs2 format
  char* pbuf = buf;

  size_t msglen = strlen(msg);
 
  char maxMsg[TPA_MAX_SIZE];           //max 140 bytes can be sent in one msg
  size_t tmp;
  size_t times;
  size_t i;
  ssize_t n;
  
  size_t num_read_ttyS0 = bufsize*2;      //num to read from ttyS0
  char tmpbuf[num_read_ttyS0];         

  pSM = initSM(phone,sms_center);		//initialize phone
  len = utf82UCS2(buf, bufsize , msg, msglen);  //convert msg from utf-8 to ucs2 format

  /* typically,max length of a msg is 140 bytes, if longer, split and send.
   * but here, max length is 86 bytes, I do not know why :( */
  int tpa_max_size = 86;
  while(len > 0)
  {
	  tmp = len > tpa_max_size ? tpa_max_size : len; 
	  memcpy(maxMsg,pbuf,tmp);  //don't use snprintf!
	  pbuf += tmp;
	  len -= tmp;
	
	  setSM_UD(pSM, maxMsg ,tmp);
	  SM2PDU(pSM, &pdu);

//	  printf("%s\n", pdu.CMGF);			//send cmgf 
	  n = write(fd, pdu.CMGF, strlen(pdu.CMGF));
	  if(strlen(pdu.CMGF) != n)
	  {
		  WriteLogHead();
		  fprintf(logfile,"write %s to %s error!\n",pdu.CMGF,serialdevice);   
		  fclose(logfile);
      destroySM(pSM);
		  return -1;
	  }
	  sleep(1);

	  n = read(fd, tmpbuf, num_read_ttyS0);				//echo to me
	  tmpbuf[n] = 0;
	  if(strstr(tmpbuf,"ERROR"))
	  {
		  WriteLogHead();
		  fprintf(logfile,"%s error,return : %s\n",pdu.CMGF,tmpbuf);
		  fclose(logfile);
		  destroySM(pSM);
		  return -4;
	  }

	  //printf("%s\n", pdu.CMGS);			//send cmgs
	  n = write(fd, pdu.CMGS, strlen(pdu.CMGS));
	  if(strlen(pdu.CMGS) != n)
	  {
		  WriteLogHead();
		  fprintf(logfile,"write %s to %s error!\n",pdu.CMGS,serialdevice);
		  fclose(logfile);
          destroySM(pSM);
		  return -1;
	  }
	  sleep(1);

	  n = read(fd, tmpbuf, num_read_ttyS0);				//echo to me
	  tmpbuf[n] = 0;
	  if(strstr(tmpbuf,"ERROR"))
	  {
		  WriteLogHead();
		  fprintf(logfile,"%s error,return : %s\n",pdu.CMGS,tmpbuf);
		  fclose(logfile);
          	  destroySM(pSM);
		  return -4;
	  }

	  times = 3;             //send for many times if something interrupt such as have a ring call
	  while(times-- > 0)
	  {
		  //printf("%s\n", pdu.PDU);				//send pdu
		  n = write(fd, pdu.PDU, strlen(pdu.PDU));
		  if(strlen(pdu.PDU) != n)
		  {
			  WriteLogHead();
			  fprintf(logfile,"write %s to %s error!\n",pdu.PDU,serialdevice);
			  fclose(logfile);
			  destroySM(pSM);
			  return -1;
		  }
		  sleep(10);
		  
		  n = read(fd, tmpbuf, num_read_ttyS0);				//echo to me
		  tmpbuf[n] = 0;
		  if(strstr(tmpbuf,"ERROR") || !strstr(tmpbuf,"OK"))         //send error,sleep for a while and send again
		  {
			  WriteLogHead();
			  fprintf(logfile,"try %d time\n%s error,return : %s\n",times,pdu.PDU,tmpbuf);
			  //destroySM(pSM);
			  sleep(15);
			  //return -4;
		  }
      else
        break;
	  }
	  if(times <= 0)
	  {
		  WriteLogHead();
		  fprintf(logfile,"%s error,return : %s\n",pdu.PDU,tmpbuf);
		  fclose(logfile);
		  destroySM(pSM);
		  return -4;
	  }
  }
  destroySM(pSM);
 // printf("msg send successfully\n");
  return 0;
}

int SetGsmModem()
{
	const int bufsize = 256;
	char buf[bufsize];
	int n;

	strcpy(buf,"at+cmee=1\r\n");          //show error num if error happens
	n = write(fd,buf,strlen(buf));
	if(strlen(buf) != n)
	{
		WriteLogHead();
		fprintf(logfile,"In SetGsmModem(), write %s error\n",serialdevice);
		return 0;
	}
	sleep(1);
	n = read(fd,buf,bufsize);
	if(strstr(buf,"ERROR"))
	{
		buf[n] = 0;
		WriteLogHead();
		fprintf(logfile,"In SetGsmModem(), %s\n",buf);
		return 0;
	}
	
	strcpy(buf,"at+cnmi=1,1,0,0,1\r\n");          //set when receive sms
	n = write(fd,buf,strlen(buf));
	if(strlen(buf) != n)
	{
		WriteLogHead();
		fprintf(logfile,"In SetGsmModem(), write /dev/ttyS0 error\n");
		return 0;
	}
	sleep(1);
	n = read(fd,buf,bufsize);
	if(strstr(buf,"ERROR"))
	{
		buf[n] = 0;
		WriteLogHead();
		fprintf(logfile,"In SetGsmModem(), %s\n",buf);
		return 0;
	}

	return 1;
}

int TestGsmModem()
{
	const int bufsize = 64;
	char buf[bufsize];
	int n;

	strcpy(buf,"at+creg?\r\n");          //test if the sim card is register to network. return 0,1 is ok. 2 is bad 
	n = write(fd,buf,strlen(buf));
	if(strlen(buf) != n)
	{
		WriteLogHead();
		fprintf(logfile,"In TestGsmModem(), write /dev/ttyS0 error\n");
		return 0;
	}
	sleep(1);
	
	n = read(fd,buf,bufsize);
	if(strstr(buf,"2") || strstr(buf,"ERROR"))
	{
		buf[n] = 0;
		WriteLogHead();
		fprintf(logfile,"%s\nsimcard not register",buf);
		return 0;
	}
	return 1; 
}

/*check the gsm modem running status,such as:
 *enough money in sim card
 */
void CheckModemStatus()
{
	const int bufsize = 1024;
	char buf[bufsize];
	
	int i,n;
	int smsIdx = 0;
	int ret = SendSMS("YE","1001"); // check money in sim card
	if(0 == ret)
	{
		sleep(5);
		n = read(fd,buf,bufsize);		
		if(n >= 12)
		{
			for(i=12;i<n;i++)
			{
				if(buf[i] < '0' || buf[i] > '9')
					break;
				else
					smsIdx = smsIdx*10 + buf[i] - '0';
			}
			sprintf(buf,"at+cmgr=%d\r\n",smsIdx);
			n = write(fd,buf,strlen(buf));
			sleep(1);
			n = read(fd,buf,bufsize);
			
		}
	}
}

/** parse configuration file */
void ParseFile (char *file) 
{
  int i;
  int fds = open(file, O_RDONLY);
  if (fds < 0) 
  {
	WriteLogHead();
	fprintf(logfile,"cannot open config file (%s) : %s\n",file,strerror(errno));
	fclose(logfile);
	exit(1);
  }
  char *tmp = readfile(fds);
  close(fds);
  // suppress commentary
  bool eff = false;
  for (i=0; tmp[i] != 0; i++) {
	switch (tmp[i]) {
	case '\n': eff = false; break;
	case '#': eff = true; // no break !!!
	default: if (eff) tmp[i] = ' ';
	}
  }
  char *posParse = tmp;
  char *tok = nextToken(&posParse);
  while (tok != NULL) {
	if(!strcasecmp(tok, "serialdevice")) {
	  strcpy(serialdevice,nextToken(&posParse));
	} else  if (!strcasecmp(tok, "host")) {
	  strcpy(host,nextToken(&posParse));
	} else if (!strcasecmp(tok, "user")) {
	  strcpy(user,nextToken(&posParse));
	} else if (!strcasecmp(tok, "passwd")) {
	  strcpy(passwd,nextToken(&posParse));
    } else if (!strcasecmp(tok, "database")) {
	  strcpy(database,nextToken(&posParse));
    } else if (!strcasecmp(tok, "msg_to_send")) {
	  strcpy(msg_to_send,nextToken(&posParse));
    } else if (!strcasecmp(tok, "msg_have_sended")) {
	  strcpy(msg_have_sended,nextToken(&posParse));
    } else if (!strcasecmp(tok, "time_interval")) {
	  tok = nextToken(&posParse);
	  time_interval = atoi(tok);
    } else if (!strcasecmp(tok, "sms_center")) {
	  strcpy(sms_center,nextToken(&posParse));
    } else {
	  WriteLogHead();	
	  fprintf(logfile,"bad configuration file : %s\n" ,tok);
	  fclose(logfile);
	  free(tmp);
	  exit(1);
	}
	tok = nextToken(&posParse);
  }
  free(tmp);
}

void WriteLogHead()
{
	time_t now;
	struct tm *timenow;

	time(&now);
	timenow = localtime(&now);
	fprintf(logfile,"%s",asctime(timenow));
}

int main(int argc, char *argv[])
{
	logfile = fopen("sms.log","a+");
	if(!logfile)
	{
		perror("open logfile error ");
		perror(strerror(errno));
		return -1;
	}

	ParseFile("sms.conf");

        struct stat statbuf;
        memset(&statbuf, 0, sizeof(statbuf));
        if (stat(serialdevice, &statbuf) != 0) {
            if (errno == ENOENT)
                strcpy(serialdevice, "/dev/ttyUSB1");
            if (stat(serialdevice, &statbuf) != 0) {
                if (errno == ENOENT) {
                    perror("stat");
                    return -1;
                }
            }
        }

	fd = open(serialdevice, O_RDWR | O_NOCTTY | O_NONBLOCK);
	if(-1 == fd)
	{
		WriteLogHead();
		fprintf(logfile,"open %s port error\n",serialdevice);
		fclose(logfile);
		return -1;
	}

	setPort(fd);

	if(!TestGsmModem())
	{
		WriteLogHead();
		fprintf(logfile,"GSM Modem test error!! quit\n");
		close(fd);
		fclose(logfile);
		return -1;
	}

	if(!SetGsmModem())
	{
		WriteLogHead();
		fprintf(logfile,"GSM Modem set error!! quit\n");
		close(fd);
		fclose(logfile);
		return -1;
	}

	mysql_init(&mysql);

	if (!mysql_real_connect(&mysql,host,user,passwd,database,0,NULL,0))
	{ 
		WriteLogHead();
		fprintf(logfile,"Error connecting to database: %s\n",mysql_error(&mysql));
		close(fd);
		fclose(logfile);
		mysql_close(&mysql);
		return ;
	}

	printf("***********sms module start to work***********\n");

	while(1)
	{
		sprintf(query,"select * from %s",msg_to_send);
		t = mysql_real_query(&mysql,query,(unsigned int) strlen(query));
		if (t)
		{
			WriteLogHead();
			fprintf(logfile,"Error making query: %s\n",mysql_error(&mysql));
			fclose(logfile);
			close(fd);
			mysql_close(&mysql);
			return;
		}

		res = mysql_store_result(&mysql); 

		if(mysql_num_rows(res) == 0)
		{
			//printf("no err msg , sleep for %d seconds\n",time_interval);
			mysql_free_result(res);     //free result after you get the result
			sleep(time_interval);
		}
		else
		{
			while(row = mysql_fetch_row(res))
			{
				ret = SendSMS(row[1],row[2]);
printf("%d",ret);
						if(0 == ret)
						{
							sprintf(query,"insert into %s(msg,phone,created_at) select msg,phone,now() from %s",msg_have_sended,msg_to_send);
              printf("%s\n",query);
							t = mysql_real_query(&mysql,query,(unsigned int) strlen(query));
							if(t)
							{
								WriteLogHead();
								fprintf(logfile,"Error making query: %s\n",mysql_error(&mysql));
								fclose(logfile);
								close(fd);
								mysql_free_result(res);
								mysql_close(&mysql);
								return -1;
							}

							sprintf(query,"delete from %s where id=%s",msg_to_send,row[0]);
              printf("%s\n",query);
							t = mysql_real_query(&mysql,query,(unsigned int) strlen(query));
							if(t)
							{
								WriteLogHead();
								fprintf(logfile,"Error making query: %s\n",mysql_error(&mysql));
								fclose(logfile);
								close(fd);
								mysql_free_result(res);
								mysql_close(&mysql);
								return -1;
							}
						}
						else if(-2 == ret || -3 == ret)
						{
							char sendbuf[256];
							sprintf(sendbuf,"./sendAlert.sh \"%s line %d ret=%d\" \"phyhwf@gmail.com\"",__FILE__,__LINE__,ret);
							system(sendbuf);				 
						}
						/* -1  write /dev/ttyS0 failure
						   -2  parameter msg is not correct
						   -3  parameter phone is wrong
						   -4  return error from AT instruction */
						else
						{
							char sendbuf[256];
							sprintf(sendbuf,"./sendAlert.sh \"%s line %d ret=%d\" \"phyhwf@gmail.com\"",__FILE__,__LINE__,ret);
							system(sendbuf);				 

							WriteLogHead();
							fprintf(logfile,"Send sms failed!! ret=%d",ret);
							fclose(logfile);
							mysql_free_result(res);
							mysql_close(&mysql);
							close(fd);
							return -1;
						}
      }//end of inner while
		  mysql_free_result(res);     //free result after you get the result
    }
	}//end outer while

	close(fd);
	fclose(logfile);
	mysql_close(&mysql);
	return 0;
}
